<?php

class AutoUpdaterImpl implements AutoUpdater
{		
	const SHARE = ' LOCK IN SHARE MODE';
	const LOCK = ' FOR UPDATE';
	
	protected $_table = 'data_table';
	
	protected $_writeRight = '';
	protected $_wRs = array();
	
	protected $_lastQueried = array();
	protected $_lastQuery = '';
	
	protected $_data = array();
	protected $_memory = array();
	protected $_delete = array();
	
	protected $_updatedCount = 0;
	protected $_deletedCount = 0;
	protected $_createdCount = 0;
	
	private $_DB = NULL;
	
	public function __construct($table = NULL) {
	    $this->_DB = DB::getInstance()->getPrimary();
		if (!empty($table)) {
			$this->_table = (string) $table;
		}
	}
	
	public function __toString()
	{
	    return sprintf("%s#%s", get_class($this), $this->_table);
	}
	
	/**
	 * @return DatabaseConnection
	 */
	public function getDB()
	{
	    return $this->_DB;
	}
	
	public function __destruct()
	{
	    if (defined('DEBUG')) echo "\nDestroying AutoUpdater#".$this->_table."\n";
        #MapperRegistry::cleanUp();
	    $this->update();
	}
	
	public function selectAll()
	{
	    $this->_select(sprintf('SELECT * FROM %s', $this->_table));
	    return $this;
	}
	
	public function selectByIDs(array $ids, $index = 'phlame_id')
	{
        if (!is_string($index) || empty($index)) {
            throw new InvalidArgumentException('index not string or empty');
        }
		$this->_select(sprintf('SELECT * FROM %s WHERE `%s` IN (%s)', $this->_table, $index, implode(',', $ids)));
		return $this;
	}
	
	public function getAll () {
		return $this->_lastQueried;
    }
    
    protected function _select($query)
    {
        if (empty($query)) {
            throw new InvalidArgumentException('Empty querystring');
        }
        if ($query != $this->_lastQuery) {
            $this->_lastQueried = $this->getDB()->query($query)->fetchAll();
            $this->_lastQuery = $query;
    		$this->_data = $this->_lastQueried + $this->_data; // addition of arrays (array_pop)
    		$this->_memory = $this->_lastQueried + $this->_memory;
    		foreach (array_keys($this->_lastQueried) as $id) {
    		    if (!isset($this->_wRs[$id]) || $this->_wRs[$id] != self::LOCK) {
        	       $this->_wRs[$id] = $this->_writeRight;
                }
    		}
        }
    }
    
    public function get(&$id = NULL)
    {
        if (empty($this->_lastQueried)) {
            throw new AutoUpdaterException('No result available');
        }
        $row = current($this->_lastQueried); # no need for a strict test (for proper programming) as we only expect arrays
        if ($row) {
            $id = $row['id'];
        }
        next($this->_lastQueried);
        return $row;
            
    }
    
    public function getIDs()
    {
    	return array_keys($this->_lastQueried);
    }
    
    public function inform(array $data)
    {
        if (!isset($data['id'])) {
            throw new InvalidArgumentException('data has no index "id"');
        }
        #if (!isset($this->_data[$data['id']])) {
        #    throw new AutoUpdaterException('Couldn\'t regcognize data [id: '.$data['id'].']');
        #}
        $this->_data[$data['id']] = $data;
    }
    
    public function setWriteRight($mode = self::LOCK) {
        switch ($mode) {
            case self::LOCK:
                $this->_writeRight = self::LOCK;
                break;
            case self::SHARE:
                $this->_writeRight = self::SHARE;
                break;
            case NULL:
            case '':
                $this->_writeRight = '';
                break;
            default:
                throw new InvalidArgumentException('Invalid parameter given!');
        }
        return $this;
    }
    
    public function update() {
        $return = false;
        if ($this->_data != $this->_memory) {
            $this->_update_create();
            $this->_update_update();
            $return = true;
        }
        $return = ($this->_update_delete() OR $return);
        return $return;
    }
    
    protected function _update_create()
    {
        $ib = array_diff_key($this->_data, $this->_memory);
        foreach ($ib as $id => $insert) {
            unset($ib[$id]['id']);
            unset($this->_data[$id]);
        }
        $this->_createdCount = count($ib);
        $this->getDB()->multiInsert($this->_table, $ib);
    }
    
    protected function _update_delete()
    {
        if (!empty($this->_delete)) {
            $this->getDB()->query(sprintf('DELETE FROM `%s` WHERE `id` IN (%s)', $this->_table, implode(',', $this->_delete)));
            $this->_deletedCount = count($this->_delete);
            return true;
        }
        return false;
    }
    
    protected function _update_update()
    {
        if (!empty($this->_data)) {
            $ub = formatBlock($this->_data,$this->_memory,'id',$this->_wRs,true);
            $this->_updatedCount = $this->getDB()->multiUpdate($this->_table, $ub);
            $this->_memory = $this->_data;
        }
    }
    
    public function delete($id)
    {
        if (!isset($this->_data[$id])) {
            throw new AutoUpdaterException('ID not found');
        }
        unset($this->_data[$id]);
        unset($this->_memory[$id]);
        $this->_delete[] = $id;
    }
    
    public function updatedCount()
    {
        return $this->_updatedCount;
    }    
    public function deletedCount()
    {
        return $this->_deletedCount;
    }    
    public function createdCount()
    {
        return $this->_createdCount;
    }
}